package com.xwh.b2c.service.impl;


import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xwh.b2c.dto.*;
import com.xwh.b2c.entity.Member;
import com.xwh.b2c.exceptions.BusinessException;
import com.xwh.b2c.handlers.BaseContextHandler;
import com.xwh.b2c.mapper.MemberMapper;
import com.xwh.b2c.service.MemberService;
import com.xwh.b2c.utils.JwtUtil;
import com.xwh.b2c.vo.MemberInfoVO;
import com.xwh.b2c.vo.MemberPageVO;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author root
 * @since 2025-07-01
 */
@Slf4j
@Service
public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements MemberService {

    // 注入JWT工具类，用于处理token
    @Resource
    private JwtUtil jwtUtil;
    // 注入HttpServletRequest，用于获取请求中的信息（如token）
    @Resource
    private HttpServletRequest request;

    // 注入微信小程序服务，用于处理微信登录等相关操作
    @Resource
    private WxMaService wxMaService;
    // 注入BCryptPasswordEncoder，用于密码加密
    @Resource
    private BCryptPasswordEncoder passwordEncoder;

    //@Resource和@Autowired的区别：按名称注入和按类型注入
//    @Resource
//    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 用户登录实现
     * 支持账号密码登录，验证用户身份并生成JWT令牌
     *
     * @param memberLoginDTO 登录信息数据传输对象
     * @return 生成的JWT令牌
     * @throws BusinessException 当账号不存在或密码错误时抛出
     */
    @Override
    public String login(MemberLoginDTO memberLoginDTO) {
        Member member = null;

        //账号密码登录
        if (memberLoginDTO.getMobile() != null) {
            // 创建查询条件
            LambdaQueryWrapper<Member> wrapper = new LambdaQueryWrapper<>();
            // 根据手机号查询用户
            wrapper.eq(Member::getMobile, memberLoginDTO.getMobile());
            // 获取用户信息
            member = this.getOne(wrapper);
            if (member == null) {
                log.info("账号不存在");
                throw new BusinessException("账号不存在或密码错误");
            }
            log.info("密码密⽂ :{}", passwordEncoder.encode(memberLoginDTO.getPassword()));
            if (!passwordEncoder.matches(memberLoginDTO.getPassword(), member.getPassword())) {
                log.info("密码错误");
                throw new BusinessException("账号不存在或密码错误");
            }
        } else {
            // 微信小程序登录流程
            String openId = null;
            //wx会话
            WxMaJscode2SessionResult session = null;
            try {
                // 调用微信API获取session信息
                session = wxMaService.getUserService().getSessionInfo(memberLoginDTO.getCode());
                // 从session中提取openId
                openId = session.getOpenid();
                if (openId == null) {
                    log.info("获取openId失败");
                    throw new BusinessException("获取openId失败");
                }
            } catch (WxErrorException e) {
                throw new BusinessException("获取openId失败");
            }


            //2.校验openid是否在数据库中
            //构建条件
            //mybatis-plus 创建空的查询条件包装器，这样就不需要使用sql语句查询
            LambdaQueryWrapper<Member> wrapper = new LambdaQueryWrapper<>();
            //        wrapper.eq(Member::getOpenId,openId);
            wrapper.eq(Member::getOpenId, openId);
            //this:当前 MemberServiceImpl 实例 用于访问 MyBatis-Plus 的通用方法
            //.getOne(wrapper)	 查询单条记录的方法
            //返回值	Member 实体对象 或 null，为查询结果
            member = this.getOne(wrapper);
            //member是null说明没注册过
            if (member == null) {//创建一个新用户
                member = new Member();
                member.setOpenId(openId);

                //写入数据库
                this.save(member);
            }

        }


        // TODO 签发token
        //用用户id生成一个token
        HashMap<String, Object> map = new HashMap<>();
        // 将用户ID存入token
        map.put("id", member.getUserId());
        // 生成JWT令牌
        String token = jwtUtil.generateToken(map);
        return token;
    }

    /**
     * 获取当前登录用户信息
     * 从请求头中获取token，解析出用户ID，然后查询用户信息
     *
     * @return 当前登录用户的信息
     */
    @Override
    public MemberInfoVO info() {
        // 从请求头获取token
//        String token = request.getHeader("token");
//        log.info("token:{}", token);
//
//        // 解析token，获取用户ID
//        Claims claims = jwtUtil.parseToken(token);
//        Integer id = claims.get("id", Integer.class);
//
//        // 根据用户ID查询用户信息
//        Member member = this.getById(id);
//        return member;

        //直接使用线程变量来获取id
        Long id = BaseContextHandler.get();
        log.info("id:{}",id);
        //
        Member member = this.getById(id);

        String mobile = member.getMobile();
        mobile = mobile.substring(0,3) + "****" + mobile.substring(7);
        //member -> MemberInfoVO
        MemberInfoVO memberInfoVO = new MemberInfoVO();
        //属性对拷：属性名称一致才能对拷  前面的复制到后面
        BeanUtils.copyProperties(member,memberInfoVO);
        memberInfoVO.setMobile(mobile);
        log.info("memberInfo:{}",memberInfoVO);
        return  memberInfoVO;
    }

    /**
     * 更新用户密码
     * 根据用户ID更新密码信息
     *
     * @param memberUpdatePasswordDTO 密码更新信息数据传输对象
     */
    @Override
    public void updatePassword(MemberUpdatePasswordDTO memberUpdatePasswordDTO) {
        String token = request.getHeader("Authorization");//header中token参数名
        log.info("token:{}",token);

        if(token == null || token.equals("")){
            throw new BusinessException("token为空");
        }

        //解析token
        Claims claims = jwtUtil.parseToken(token);
        Integer id = claims.get("id", Integer.class);
        log.info("id:{}", id);
        Member member = this.getById(id);
        if(member == null){
            throw new BusinessException("用户不存在");
        }

        //验证旧密码和原来是否匹配
        if(!passwordEncoder.matches(memberUpdatePasswordDTO.getOldPassword(),member.getPassword())) {
            log.info("旧密码错误");
            throw new BusinessException("旧密码错误");
        }

        member.setPassword(passwordEncoder.encode(memberUpdatePasswordDTO.getNewPassword()));
        this.updateById(member);
    }

    @Override
    public Member register(MemberRegisterDTO memberRegisterDTO) {
        if(!StringUtils.hasText(memberRegisterDTO.getCode())){
            throw new BusinessException("验证码为空");
        }

        if(!redisTemplate.hasKey("code")){
            throw new BusinessException("验证码已过期");
        }
        //验证码是否正确
        String code = redisTemplate.opsForValue().get("code");

        if(code == null){
            throw new BusinessException("验证码已过期");
        }
        if(!code.equals(memberRegisterDTO.getCode())){
            throw new BusinessException("验证码错误");
        }
        //检查手机号是否已经注册 & 检查邮箱是否已经注册
        //注意这里的泛型是 Member
        LambdaQueryWrapper<Member> wrapper = new LambdaQueryWrapper<>();
        //StringUtils导包: import org.springframework.util.StringUtils;
        wrapper.eq(StringUtils.hasText(memberRegisterDTO.getMobile()),Member::getMobile,memberRegisterDTO.getMobile());
        wrapper.eq(StringUtils.hasText(memberRegisterDTO.getEmail()),Member::getEmail,memberRegisterDTO.getEmail());
        Member member = this.getOne(wrapper);

        //已经被注册过
        if(member!=null){
            //手机注册的
            if(StringUtils.hasText(memberRegisterDTO.getMobile())){
                throw new BusinessException("手机号已注册");
            }
            //邮箱注册的
            if(StringUtils.hasText(memberRegisterDTO.getEmail())){
                throw new BusinessException("邮箱已注册");
            }
        }
        //检查密码是否合法
        if(!StringUtils.hasText(memberRegisterDTO.getPassword())){
            throw new BusinessException("密码不能为空");
        }

        //能够进行注册 说明以上获取到的member是Null 需要创建一个实例
        member = new Member();
        member.setEmail(memberRegisterDTO.getEmail());
        member.setMobile(memberRegisterDTO.getMobile());
        //加密密码
        member.setPassword(passwordEncoder.encode(memberRegisterDTO.getPassword()));

        this.save(member);
        redisTemplate.delete("code");

        //看一下ID
        log.info("member:{}",member);

        return member;
    }

    @Override
    public void bindPhoneOrEmailOrOpenId(MemberBindAccountDTO memberBindAccountDTO) {

    }

    @Override
    public IPage<MemberPageVO> getPage(MemberPageQueryDTO memberPageQueryDTO) {
        //先根据传过来的条件进行查询
        LambdaQueryWrapper<Member> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.hasText(memberPageQueryDTO.getMobile()), Member::getMobile, memberPageQueryDTO.getMobile());
        wrapper.eq(StringUtils.hasText(memberPageQueryDTO.getEmail()), Member::getEmail, memberPageQueryDTO.getEmail());
        wrapper.eq(StringUtils.hasText(memberPageQueryDTO.getTrueName()), Member::getTrueName, memberPageQueryDTO.getTrueName());
        wrapper.eq(memberPageQueryDTO.getStatus() != null, Member::getStatus, memberPageQueryDTO.getStatus());

        /*
        * Mybatis-plus提供的方法
        * 返回分页数据，内容包括    page.current page.pageSize,page.total page.records（符合条件的内容）
        * */
        Page<Member> page = this.page(new Page<>(memberPageQueryDTO.getCurrent(), memberPageQueryDTO.getPageSize()), wrapper);
        // records中存储数据的类型是Member
        // 需要把member转换成MemberPageVO

        List<Member> records = page.getRecords();
        Page<MemberPageVO> pageVO = new Page<>();
        BeanUtils.copyProperties(page,pageVO);

        List<MemberPageVO> pageVOList = new ArrayList<>();
        for (Member record : records) {
            MemberPageVO memberPageVO = new MemberPageVO();
            BeanUtils.copyProperties(record,memberPageVO);
            pageVOList.add(memberPageVO);
        }

        /*
        * IPage的I表示是一个接口 需要实现类 Page来实现
        * */

        //用一个新的 page对象来返回memberPageVO的记录
        pageVO.setRecords(pageVOList);
        log.info("page:{}",page);
        return pageVO;
    }

    @Override
    public void enable(Member member) {
        if(member == null){
            throw new BusinessException("用户不存在");
        }
        Boolean updateStatus = !member.getStatus();
        member.setStatus(updateStatus);

        this.updateById(member);
    }

}
